package redis

import (
	"context"
	"fmt"
	"regexp"

	//"tour/cmd"

	"strconv"
	"time"

	. "github.com/go-redis/redis/v8"

	"sort"
)

//var rdb *redis.Client

type redCmd struct {
	IP         string
	Port       string
	Addr       string
	KillIP     string
	DelPattern string
	TimeWait   float32
}

var RedisPassword string

// 获取传递的password， 默认值是""
func Password() string {
	return RedisPassword
}

// 初始化redis连接
func initClient(addr string) (rdb *Client, err error) {
	// 通过 redis.NewClient 函数即可创建一个 redis 客户端, 这个方法接收一个 redis.Options 对象参数, 通过这个参数, 我们可以配置 redis 相关的属性, 例如 redis 服务器地址, 数据库名, 数据库密码等。
	rdb = NewClient(&Options{
		Addr:     addr,
		Password: Password(), // no password set
		DB:       0,          // use default DB
	})
	// 通过 client.Ping() 来检查是否成功连接到了 redis 服务器
	_, err = rdb.Ping(context.Background()).Result()
	if err != nil {
		return nil, err
	}
	return rdb, nil
}

func ListByClient(addr string) string {
	rdb, err := initClient(addr)
	if err != nil {
		return "连接不上redis"
	}
	defer rdb.Close()
	return rdb.ClientList(context.Background()).Val()
}

func GroupByClient(addr string) string {
	var result string
	var resultMap = make(map[string]int)

	type redisR struct {
		addr string
		num  int
	}
	var redisList = make([]*redisR, 0)

	rdb, err := initClient(addr)
	if err != nil {
		return "连接不上redis"
	}
	defer rdb.Close()

	listClient := rdb.ClientList(context.Background()).Val()

	rex := regexp.MustCompile("id=.*addr=(.*?):.*\n")
	subMatchs := rex.FindAllStringSubmatch(listClient, -1) //匹配括号里的
	for _, v := range subMatchs {
		if _, ok := resultMap[v[1:][0]]; !ok {
			resultMap[v[1:][0]] = 0
		}
		resultMap[v[1:][0]]++
	}

	for k, v := range resultMap {
		a := &redisR{addr: k, num: v}
		redisList = append(redisList, a)
	}

	sort.SliceStable(redisList, func(i int, j int) bool {
		return redisList[i].num > redisList[j].num
	})

	// 已经按照索引排序
	for _, val := range redisList {
		res := val.addr + " :: " + strconv.Itoa(val.num)
		result += res + "\n"
	}
	return result
}

func KillNormal(addr string) string {
	rdb, err := initClient(addr)
	if err != nil {
		return "连接不上redis"
	}
	defer rdb.Close()

	return fmt.Sprintf("%d clients killed", rdb.ClientKillByFilter(context.Background(), "type", "normal").Val())
}

func KillByIP(addr string, kill string) string {
	return "todo"
}

func DelScan(addr string, delPattern string, wait int) string {
	if delPattern == "" {
		return "delPattern is empty, please check it, use -d to set it"
	}

	var result int64
	var cursor uint64
	var n int
	rdb, err := initClient(addr)
	if err != nil {
		return "连接不上redis"
	}
	defer rdb.Close()
	ctx := context.Background()
	for {
		var keys []string
		var err error
		//*扫描所有key，每次500条
		keys, cursor, err = rdb.Scan(ctx, cursor, delPattern, 500).Result()
		if err != nil {
			panic(err)
		}
		n = len(keys)

		result += int64(n)
		// var value string
		// for _, key := range keys {
		//         value, err = client.Del(key).Result()
		//         fmt.Printf("%v %v\n", key, value)
		// }
		rdb.Del(ctx, keys...)
		WaitSleep(wait)
		if cursor == 0 {
			break
		}
	}
	return fmt.Sprintf("%d keys del .", result)
}

func WaitSleep(t int) {
	if t > 0 {
		time.Sleep(time.Duration(t) * time.Millisecond)
	}
}

// 模糊匹配key，获取value
func ScanDataRedis(addr, keyPattern string, wait int) string {
	rdb, err := initClient(addr)
	if err != nil {
		return "init Redis 失败: " + err.Error()
	}
	return ScanData(rdb, keyPattern, wait)
}

// 一下是和哨兵公用的函数
func ScanData(rdb *Client, keyPattern string, wait int) string {
	var result int64
	var cursor uint64
	var n int
	defer rdb.Close()
	ctx := context.Background()
	for {
		var keys []string
		var err error
		//*扫描所有key，每次500条
		keys, cursor, err = rdb.Scan(ctx, cursor, keyPattern, 500).Result()
		if err != nil {
			panic(err)
		}
		n = len(keys)

		result += int64(n)
		var value string
		for _, key := range keys {
			tp := rdb.Type(ctx, key).Val()
			switch tp {
			case "string":
				if rdb.StrLen(ctx, key).Val() < 1000 {
					value, err = rdb.Get(ctx, key).Result()
					if err != nil {
						value = err.Error()
					}
				} else {
					value = "bigKey"
				}
			default:
				value = "notString"
			}
			fmt.Printf("key=%s || value=%s || type=%s\n", key, value, tp)
		}
		WaitSleep(wait)
		if cursor == 0 {
			break
		}
	}
	return string(result) + " keys:"
}
